/*
 *  Copyright 2008-2009 NVIDIA Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#pragma once

#include <sfv/csr_matrix.h>
#include <sfv/exception.h>
#include <sfv/detail/format_utils.h>

#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <map>

#include <fstream>

#define OUT_FILE_TYPE std::ostream&

#define CREATE_OUT_FILE(file,filename) std::ofstream file(filename);

#define WRITE_OUT_FILE(file,data) file << data;

#define READ_FILE(stream,filename) do{ \
                                        std::ifstream infile(filename); \
                                        stream << infile.rdbuf(); \
                                        infile.close(); \
                                }while(0);

namespace sfv {
namespace io {

inline
void tokenize(std::vector<std::string>& tokens, const std::string& str,
		const std::string& delimiters = "\n\r\t ") {
	// Skip delimiters at beginning.
	std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
	// Find first "non-delimiter".
	std::string::size_type pos = str.find_first_of(delimiters, lastPos);

	while (std::string::npos != pos || std::string::npos != lastPos) {
		// Found a token, add it to the vector.
		tokens.push_back(str.substr(lastPos, pos - lastPos));
		// Skip delimiters.  Note the "not_of"
		lastPos = str.find_first_not_of(delimiters, pos);
		// Find next "non-delimiter"
		pos = str.find_first_of(delimiters, lastPos);
	}
}

template<typename IndexType, typename ValueType>
void read_matrix_market_file(sfv::csr_matrix<IndexType, ValueType>& csr,
		const std::string& filename) {
	std::stringstream file(std::stringstream::in | std::stringstream::out);

    /*std::ifstream infile(filename); \
    stream << infile.rdbuf(); \
    infile.close();*/

	READ_FILE(file,filename.c_str())

	if (!file.good())
		throw sfv::exception("invalid file name");

	read_matrix_market_stream(csr, file);
}

template<typename IndexType, typename ValueType>
void read_matrix_market_stream(sfv::csr_matrix<IndexType, ValueType>& csr,
		std::istream& file) {
	// read file contents line by line
	std::string line;

	size_t num_rows, num_cols, num_entries;

	// skip over banner and comments
	do {
		std::getline(file, line);

		std::vector<std::string> tokens;
		tokenize(tokens, line);

		if (tokens[1].compare("Nodes:") == 0) {
			std::istringstream(tokens[2]) >> num_rows;
			std::istringstream(tokens[2]) >> num_cols;
			std::istringstream(tokens[4]) >> num_entries;
		}
	} while (line[0] == '#');

	// line contains [num_rows num_columns]
	std::vector<std::string> tokens;
	tokenize(tokens, line);

	if (tokens.size() != 2)
		throw sfv::io_exception("invalid MatrixMarket CSR format");

	csr.resize(num_rows, num_cols, num_entries);

	size_t num_entries_read = 0;

	sfv::array1d<IndexType> row_indices(num_entries);

	std::multimap<IndexType,IndexType> edge_map;
	std::map<IndexType,IndexType> translate;

	size_t index = 0;
	// read file contents
	while (num_entries_read < csr.num_entries && !file.eof()) {
		IndexType from, to;

		file >> from;
		file >> to;

		edge_map.insert(std::pair<IndexType, IndexType>(from, to));
		num_entries_read++;

		if( translate.end() == translate.find(from) )
			translate[from] = index++;
	}

	index = 0;
	for (std::multimap<int,int>::iterator it = edge_map.begin(); it != edge_map.end(); ++it)
	{
		row_indices[index] = translate[it->first];
		csr.column_indices[index++] = translate[it->second];
	}

	sfv::detail::indices_to_offsets(row_indices, csr.row_offsets);

	std::fill(csr.values.begin(), csr.values.end(), ValueType(1));
}

template<typename MatrixType>
void read_matrix_market_file(MatrixType& mtx, const std::string& filename) {
	typedef typename MatrixType::index_type IndexType;
	typedef typename MatrixType::value_type ValueType;

	sfv::io::read_matrix_market_file(mtx, filename);
}

} //end namespace io
} //end namespace sfv

